package com.rerun.common.quartz;

import com.rerun.entity.quartz.QrtzSchedulerState;
import com.rerun.mapper.quartz.SchedulerInstanceMapper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * quartz 管理中心
 *
 * @author dax.
 * @version v1.0
 * @since 2017 /10/24 13:10
 */
public class QuartzManagerCenter implements QuartzManager {
    private static final Logger log = LoggerFactory.getLogger(QuartzManagerCenter.class);

    private Scheduler scheduler;

    private SchedulerInstanceMapper schedulerInstanceMapper;
    @Override
    public List<QrtzSchedulerState> getAllQuartzInstances() {
        return schedulerInstanceMapper.getAllQuartzInstances();
    }

    @Override
    public List<QuartzJobBean> getAllJobs() throws SchedulerException {
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
        List<QuartzJobBean> jobList = new ArrayList<>();
        for (JobKey jobKey : jobKeys) {
            List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
            for (Trigger trigger : triggers) {
                Date nextTime = trigger.getNextFireTime();
                Date previousFireTime = trigger.getPreviousFireTime();

                JobDetail jobDetail = scheduler.getJobDetail(jobKey);
                String jobClassName = jobDetail.getJobClass().getName();
                String description = jobDetail.getDescription();

                String jobName = jobKey.getName();
                String jobGroupName = jobKey.getGroup();

                TriggerKey triggerKey = trigger.getKey();
                String triggerGroupName = triggerKey.getGroup();
                String triggerName = triggerKey.getName();

                //触发器当前状态
                Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);
                String status = triggerState.name();
                // 装载属性
                QuartzJobBean job = new QuartzJobBean();
                job.setTriggerName(triggerName);
                job.setTriggerGroupName(triggerGroupName);
                job.setJobClassName(jobClassName);
                job.setJobName(jobName);
                job.setJobGroupName(jobGroupName);
                job.setDescription(description);
                //上次触发时间
                job.setPreviousTime(previousFireTime);
                //下次触发时间
                job.setNextTime(nextTime);
                job.setStatus(status);

                if (trigger instanceof CronTrigger) {
                    CronTrigger cronTrigger = (CronTrigger) trigger;
                    String cronExpression = cronTrigger.getCronExpression();
                    job.setCron(cronExpression);
                }
                jobList.add(job);
            }
        }
        return jobList;
    }

    @Override
    public QuartzJobBean getJob(String jobName, String jobGroupName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
        // 检测key 是否存在  不存在直接返回
        if (!scheduler.checkExists(jobKey)) {
            return null;
        }
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        String jobClassName = jobDetail.getJobClass().getName();
        String description = jobDetail.getDescription();

        QuartzJobBean job = new QuartzJobBean();
        List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
        if (CollectionUtils.isNotEmpty(triggers)) {
            Trigger trigger = triggers.get(0);
            Date nextTime = trigger.getNextFireTime();
            Date previousFireTime = trigger.getPreviousFireTime();

            TriggerKey triggerKey = trigger.getKey();
            String triggerGroupName = triggerKey.getGroup();
            String triggerName = triggerKey.getName();

            //触发器当前状态
            Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);
            String status = triggerState.name();

            job.setTriggerName(triggerName);
            job.setTriggerGroupName(triggerGroupName);
            //上次触发时间
            job.setPreviousTime(previousFireTime);
            //下次触发时间
            job.setNextTime(nextTime);
            job.setStatus(status);

            if (trigger instanceof CronTrigger) {
                CronTrigger cronTrigger = (CronTrigger) trigger;
                String cronExpression = cronTrigger.getCronExpression();
                job.setCron(cronExpression);
            }
        }

        job.setJobClassName(jobClassName);
        job.setJobName(jobName);
        job.setJobGroupName(jobGroupName);
        job.setDescription(description);
        return job;
    }

    @Override
    public List<QuartzJobBean> getRunningJobs() throws SchedulerException {
        List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
        List<QuartzJobBean> jobList = new ArrayList<>(executingJobs.size());
        for (JobExecutionContext executingJob : executingJobs) {

            JobDetail jobDetail = executingJob.getJobDetail();
            String jobClassName = jobDetail.getJobClass().getName();
            String description = jobDetail.getDescription();

            JobKey jobKey = jobDetail.getKey();
            String jobName = jobKey.getName();
            String jobGroupName = jobKey.getGroup();

            Trigger trigger = executingJob.getTrigger();
            Date nextTime = trigger.getNextFireTime();
            Date previousFireTime = trigger.getPreviousFireTime();
            TriggerKey triggerKey = trigger.getKey();
            String triggerGroupName = triggerKey.getGroup();
            String triggerName = triggerKey.getName();

            //触发器当前状态
            Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);
            String status = triggerState.name();

            QuartzJobBean job = new QuartzJobBean();
            job.setJobName(jobName);
            job.setJobGroupName(jobGroupName);
            job.setDescription(description);
            job.setJobClassName(jobClassName);
            job.setTriggerName(triggerName);
            job.setTriggerGroupName(triggerGroupName);
            job.setStatus(status);
            //上次触发时间
            job.setPreviousTime(previousFireTime);
            //下次触发时间
            job.setNextTime(nextTime);

            if (trigger instanceof CronTrigger) {
                CronTrigger cronTrigger = (CronTrigger) trigger;
                String cronExpression = cronTrigger.getCronExpression();
                job.setCron(cronExpression);
            }
            jobList.add(job);
        }
        return jobList;
    }


    @Override
    @SuppressWarnings("unchecked")
    public void addJob(QuartzJobBean jobBean) throws ClassNotFoundException {
        String jobClassName = jobBean.getJobClassName();
        String jobName = jobBean.getJobName();
        String jobGroupName = jobBean.getJobGroupName();
        String description = jobBean.getDescription();
        // 触发器时间设定
        String cron = jobBean.getCron();

        // 任务名，任务组，任务执行类
        JobBuilder jobBuilder = JobBuilder.newJob((Class<? extends Job>) Class.forName(jobClassName));
        JobDetail jobDetail = jobBuilder.withIdentity(jobName, jobGroupName).withDescription(description).build();
        // 触发器
        TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
        // 触发器名,触发器组
        String triggerName = jobBean.getTriggerName();
        String triggerGroupName = jobBean.getTriggerGroupName();
        triggerBuilder.withIdentity(triggerName, triggerGroupName);
        triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
        triggerBuilder.startNow();
        // 创建Trigger对象
        CronTrigger trigger = (CronTrigger) triggerBuilder.build();
        try {    // 调度容器设置JobDetail和Trigger
            scheduler.scheduleJob(jobDetail, trigger);
            scheduler.getSchedulerInstanceId();
            // 启动
            if (!scheduler.isShutdown()) {
                scheduler.start();
            }
            log.info(" ADD JOB -- JOBNAME: {}", jobName);
        } catch (Exception e) {
            log.debug("   FAIL TO ADD JOB --JOBNAME: " + jobName, e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public void modifyJobCron(QuartzJobBean jobBean) {
        String jobName = jobBean.getJobName();
        String jobGroupName = jobBean.getJobGroupName();
        // 触发器名,触发器组
        String triggerName = jobBean.getTriggerName();
        String triggerGroupName = jobBean.getTriggerGroupName();
        String cron = jobBean.getCron();

        try {
            JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
            // key校验是否存在      表达式校验
            if (!scheduler.checkExists(jobKey) || !isValidExpression(cron)) {
                return;
            }

            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
            Trigger trigger = scheduler.getTrigger(triggerKey);
            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
            //获取trigger
            CronTrigger cronTrigger = (CronTrigger) trigger;
            //按新的cronExpression表达式重新构建trigger
            cronTrigger = cronTrigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            //按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, cronTrigger);
            log.info(" MODIFY JOB CRON -- JOBNAME: {}", jobName);
        } catch (SchedulerException e) {
            log.debug("  FAIL TO MODIFY JOB CRON  --JOBNAME: " + jobName, e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public void removeJob(QuartzJobBean jobBean) {
        String triggerName = jobBean.getTriggerName();
        String triggerGroupName = jobBean.getTriggerGroupName();
        if (!StringUtils.isEmpty(triggerName)) {
            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
            String jobName = jobBean.getJobName();
            String jobGroupName = jobBean.getJobGroupName();
            try {
                scheduler.pauseTrigger(triggerKey);// 停止触发器
                scheduler.unscheduleJob(triggerKey);// 移除触发器
                scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));// 删除任务
                log.info(" REMOVE JOB -- JOBNAME: {}", jobName);
            } catch (SchedulerException e) {
                log.debug("  FAIL TO REMOVE JOB --JOBNAME: " + jobName, e);
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public void pauseJob(String jobName, String jobGroupName) {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
        try {
            scheduler.pauseJob(jobKey);
            log.info(" PAUSE JOB -- JOBNAME: {}", jobName);
        } catch (SchedulerException e) {
            log.debug("  FAIL TO PAUSE JOB --JOBNAME: " + jobName, e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public void pauseAllJobs() {
        try {
            scheduler.pauseAll();
            log.info(" PAUSE ALL JOBS");
        } catch (SchedulerException e) {
            log.debug("  FAIL TO PAUSE ALL JOBS", e);
            throw new RuntimeException(e);
        }
    }


    @Override
    public void shutdownAllJobs() {
        shutdown(true);
    }

    @Override
    public void forceShutdownAllJobs() {
        shutdown(false);
    }

    @Override
    public void resumeJob(String jobName, String jobGroupName) {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
        try {
            scheduler.resumeJob(jobKey);
            log.info(" RESUME JOB -- JOBNAME: {}", jobName);
        } catch (SchedulerException e) {
            log.debug("  FAIL TO RESUME JOB --JOBNAME: " + jobName, e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public void resumeAllJobs() {
        try {
            scheduler.resumeAll();
            log.info(" RESUME ALL JOBS");
        } catch (SchedulerException e) {
            log.debug("  FAIL TO RESUME ALL JOBS", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 判断cron时间表达式正确性
     *
     * @param cronExpression the cron expression
     * @return boolean
     */
    private boolean isValidExpression(final String cronExpression) {
        CronTriggerImpl trigger = new CronTriggerImpl();
        try {
            trigger.setCronExpression(cronExpression);
            Date date = trigger.computeFirstFireTime(null);
            return date != null && date.after(new Date());
        } catch (ParseException e) {
            log.debug("cron is Illegal");
        }
        return false;
    }

    private void shutdown(boolean waitForJobsToComplete) {
        try {
            if (!scheduler.isShutdown()) {
                scheduler.shutdown(waitForJobsToComplete);
            }
        } catch (Exception e) {
            log.debug("quartz: 关闭所有定时任务异常", e);
            throw new RuntimeException(e);
        }
    }

    public Scheduler getScheduler() {
        return scheduler;
    }

    public void setScheduler(Scheduler scheduler) {
        this.scheduler = scheduler;
    }

    public SchedulerInstanceMapper getSchedulerInstanceMapper() {
        return schedulerInstanceMapper;
    }

    public void setSchedulerInstanceMapper(SchedulerInstanceMapper schedulerInstanceMapper) {
        this.schedulerInstanceMapper = schedulerInstanceMapper;
    }
}
